import type { IRawMsg } from "@/chatlog_types/IRawMsg";
import Callbacks from "@/utils/Callbacks";
import { Poller } from "@/utils/Poller";
import { Chat } from "@/views/Chat";
import dayjs, { Dayjs } from "dayjs";
import { MessageLoader } from "./MessageLoader";
export interface IChatMgrCallbacks<Mgr extends BaseChatMgr = BaseChatMgr> {
  on_changed?(mgr: Mgr): void;
  on_date_range_changed?(mgr: Mgr, start_date: Dayjs, end_date: Dayjs): void;
  on_loading_news?(mgr: Mgr, loading: boolean): void;
  on_loading_history?(mgr: Mgr, loading: boolean): void;
  on_msgs_added?(mgr: Mgr, msgs: Chat.Message<IRawMsg>[]): void;
}
export interface ISession {
  userName: string;
  account?: string;
}
export abstract class BaseChatMgr {
  __debugging = false;
  static readonly TAG: string = 'BaseChatMgr';
  readonly TAG: string = BaseChatMgr.TAG
  readonly day_range: number = 5 * 365;
  readonly callbacks = new Callbacks<IChatMgrCallbacks<BaseChatMgr>>();
  readonly session: ISession;
  abstract readonly loader: MessageLoader;
  abstract get latest(): Chat.Message<IRawMsg> | undefined;
  abstract get earliest(): Chat.Message<IRawMsg> | undefined;
  start_date: Dayjs = dayjs();
  end_date: Dayjs = dayjs()
  history_loading: boolean = false;
  readonly poller = new Poller(() => this.poll())

  private _load_history_jid: number = 0;
  protected async poll(): Promise<void> {
    return this.messages_length ? this.load_news() : this.load_history();
  }
  abstract get messages_length(): number;
  abstract get messages(): Chat.Message<IRawMsg>[];
  abstract clear_msgs(): void
  abstract append_msgs(msgs: Chat.Message<IRawMsg>[]): void
  abstract insert_msgs(index: number, msgs: Chat.Message<IRawMsg>[]): void
  abstract set_msgs(msgs: Chat.Message<IRawMsg>[]): void
  scroll_to_bottom(behavior: "auto" | "instant" | "smooth" = 'smooth') { }
  is_reach_bottom(): boolean { return true }
  before_history_load() { }
  after_history_load() { }

  constructor(session: ISession) {
    this.session = session;
  }

  start() {
    this.poller.start()
  }
  reset() {
    this.poller.stop()
    this.history_loading = false;
    this.loader.reset?.();
    this.clear_msgs();
    this.callbacks.emit('on_changed')(this);
    ++this._load_history_jid;
  }
  protected async load_history() {
    if (this.history_loading) return;
    const load_history_jid = ++this._load_history_jid;
    this.history_loading = true;
    this.before_history_load();

    const { day_range } = this;
    const end_date = dayjs().endOf('day');
    const start_date = end_date.add(-day_range, 'day').startOf('day');
    const start_date_text = this.start_date.format('YYYY-MM-DD');
    const end_date_text = this.end_date.format('YYYY-MM-DD');
    if (
      start_date_text !== start_date.format('YYYY-MM-DD') ||
      end_date_text !== end_date.format('YYYY-MM-DD')
    ) {
      this.end_date = end_date
      this.start_date = start_date
      this.callbacks.emit('on_date_range_changed')(this, start_date, end_date);
    }
    this.callbacks.emit('on_loading_history')(this, true);
    return this.loader.load_history().then((list) => {
      if (load_history_jid !== this._load_history_jid) return;
      if (!list.length) return this.on_history_done?.()


      const originally_empty = !this.messages_length
      if (originally_empty) {
        const msgs = list.reverse()
        this.callbacks.emit('on_msgs_added')(this, msgs);
        this.set_msgs(msgs);
        this.callbacks.emit('on_changed')(this);
        this.scroll_to_bottom('instant')
      } else {
        const msgs = list.reverse()
        this.callbacks.emit('on_msgs_added')(this, msgs);
        this.insert_msgs(0, msgs);
        this.callbacks.emit('on_changed')(this);
      }
      this.loader.on_history_accepted?.()
    }).catch(e => {
      console.error(`[${this.TAG}.load_history] ${e}`)
    }).finally(() => {
      this.history_loading = false;
      this.after_history_load();
      this.callbacks.emit('on_loading_history')(this, false);
    })
  }
  on_history_done?(): void;

  /**
   * 获取新消息
   *
   * @protected
   * @return {Promise<void>}
   * @memberof BaseChatMgr
   */
  protected async load_news(): Promise<void> {
    this.callbacks.emit('on_loading_news')(this, true);
    const { jid } = this.poller;
    this.loader.load_news().then((msgs) => {
      if (jid != this.poller.jid) return;
      const is_reach_bottom = this.is_reach_bottom();
      this.callbacks.emit('on_msgs_added')(this, msgs);
      this.append_msgs(msgs);
      this.callbacks.emit('on_changed')(this);
      if (is_reach_bottom) this.scroll_to_bottom();
      this.loader.on_news_accepted?.()
    }).catch((e) => {
      if (jid != this.poller.jid) return;
      return Promise.reject(e)
    }).finally(() => {
      if (jid != this.poller.jid) return;
      this.callbacks.emit('on_loading_news')(this, false);
    })
  }
}

